package com.aliyun.heiyu.common.source;

import com.alibaba.druid.pool.DruidDataSource;
import com.aliyun.heiyu.common.util.PropertiesUtil;
import com.aliyun.heiyu.common.util.SM3Util;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ConnectionManager {
    private static Logger logger = LoggerFactory.getLogger(ConnectionManager.class);

    private static volatile ConnectionManager instance;

    private static DruidDataSource dataSourceMaster;
    private static DruidDataSource dataSourceSlave;
    private static ThreadLocal<Connection> tl = new ThreadLocal<>();
    private static ThreadLocal<Integer> tl_connCount = new ThreadLocal<>();
    private static Properties dbProps;

    //主从开关
    private static boolean jdbc_switch;

    public static ConnectionManager getInstance() {
        synchronized (ConnectionManager.class) {
            if (instance == null) {
                instance = new ConnectionManager();
            }
            return instance;
        }
    }

    public static Properties check() {
        Properties props = PropertiesUtil.getProperties("jdbcConfig.properties");
        Properties propsB = PropertiesUtil.getProperties("dbBak.properties");
        String masterName = props.getProperty("master.jdbc.user");
        String masterPa = props.getProperty("master.jdbc.password");
        String slaveName = props.getProperty("slave.jdbc.user");
        String slavePa = props.getProperty("slave.jdbc.password");
        String dbPrivilege = props.getProperty("privileges.database");
        boolean ret = true;
        if (!checkData(masterName)) {
            ret &= false;
            props.setProperty("master.jdbc.user", propsB.getProperty("master.jdbc.user"));
        }
        if (!checkData(masterPa)) {
            ret &= false;
            props.setProperty("master.jdbc.password", propsB.getProperty("master.jdbc.password"));
        }
        if (!checkData(slaveName)) {
            ret &= false;
            props.setProperty("slave.jdbc.user", propsB.getProperty("slave.jdbc.user"));
        }
        if (!checkData(slavePa)) {
            ret &= false;
            props.setProperty("slave.jdbc.password", propsB.getProperty("slave.jdbc.password"));
        }
        if (!checkData(dbPrivilege)) {
            ret &= false;
            props.setProperty("privileges.database", propsB.getProperty("privileges.database"));
        }
        //判断  更新源文件
        if (!ret) {
            PropertiesUtil.updateProperties("jdbcConfig.properties", props);
        }
        //Properties properties = PropertiesUtil.getProperties("jdbcConfig.properties");
        Properties simpty = simpty(props);
        dbProps = simpty;

        return simpty;
    }

    public static Properties simpty(Properties props) {
        String masterName = props.getProperty("master.jdbc.user");
        String[] split = masterName.split(",");
        masterName = split[0];
        String masterPa = props.getProperty("master.jdbc.password");
        String[] split1 = masterPa.split(",");
        masterPa = split1[0];
        String dbPri = props.getProperty("privileges.database");
        String[] split2 = dbPri.split(",");
        dbPri = split2[0];
        props.setProperty("master.jdbc.user", masterName);
        props.setProperty("master.jdbc.password", masterPa);
        props.setProperty("privileges.database", dbPri);
        String slaveName = props.getProperty("slave.jdbc.user");
        String[] split3 = slaveName.split(",");
        slaveName = split3[0];
        String slavePa = props.getProperty("slave.jdbc.password");
        String[] split4 = slavePa.split(",");
        slavePa = split4[0];
        props.setProperty("slave.jdbc.user", slaveName);
        props.setProperty("slave.jdbc.password", slavePa);
        return props;
    }

    public static boolean checkData(String inputData) {
        String[] split = inputData.split(",");
        if (split.length < 2) {
            return false;
        } else {
            String oriName = split[0];
            String sign1 = split[1];
            String sign2 = SM3Util.convertEncryption(oriName);
            if (!sign1.equals(sign2)) {
                return false;
            }
        }
        return true;
    }

    private ConnectionManager() {
        try {
            Properties props = check();
            String encryptUsername = props.getProperty("master.jdbc.user");
            String encryptPassword = props.getProperty("master.jdbc.password");
            String jdbcUrl = props.getProperty("master.jdbc.url");
            String driverClassName = props.getProperty("master.jdbc.driver");
            dataSourceMaster = new MyDruidDataSource();
            dataSourceMaster.setDriverClassName(driverClassName);
            dataSourceMaster.setUrl(jdbcUrl);
            dataSourceMaster.setPasswordCallback(new DbPasswordCallback(encryptPassword));
            dataSourceMaster.setUserCallback(new DbUsernameCallback(encryptUsername));
            encryptPassword = null;
            encryptUsername = null;
            dataSourceMaster.setInitialSize(10);
            dataSourceMaster.setMinIdle(5);
            dataSourceMaster.setMaxActive(50);
            dataSourceMaster.setMaxWait(3000);
            dataSourceMaster.setMaxOpenPreparedStatements(100);
            dataSourceMaster.setTimeBetweenEvictionRunsMillis(60000);
            String encryptUsername1 = props.getProperty("slave.jdbc.user");
            String encryptPassword1 = props.getProperty("slave.jdbc.password");
            String jdbcUrl1 = props.getProperty("slave.jdbc.url");
            String driverClassName1 = props.getProperty("slave.jdbc.driver");
            dataSourceSlave = new MyDruidDataSource();
            dataSourceSlave.setDriverClassName(driverClassName1);
            dataSourceSlave.setUrl(jdbcUrl1);
            dataSourceSlave.setPasswordCallback(new DbPasswordCallback(encryptPassword1));
            dataSourceSlave.setUserCallback(new DbUsernameCallback(encryptUsername1));
            encryptPassword1 = null;
            encryptUsername1 = null;
            dataSourceSlave.setInitialSize(10);
            dataSourceSlave.setMinIdle(5);
            dataSourceSlave.setMaxActive(50);
            dataSourceSlave.setMaxWait(3000);
            dataSourceSlave.setMaxOpenPreparedStatements(100);
            dataSourceSlave.setTimeBetweenEvictionRunsMillis(60000);
            jdbc_switch = true;
            jdbcMasterSlaveMonitor();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }

    public void jdbcMasterSlaveMonitor() {
        try {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    checkJdbc();
                    logger.info("得到现在的jdbc状态：" + jdbc_switch);
                }
            };
            ScheduledExecutorService service = new ScheduledThreadPoolExecutor(1, new BasicThreadFactory.Builder().namingPattern("jdbc-master-slave-pool-%d").daemon(true).build());
            service.scheduleAtFixedRate(runnable, 0, 60, TimeUnit.SECONDS);
        } catch (Exception e) {
            logger.error("数据库监控线程启动失败");
        }
    }

    private void checkJdbc() {
        if (!checkJdbcDataSource(dataSourceMaster)) {
            if (checkJdbcDataSource(dataSourceSlave)) {
                jdbc_switch = false;
            } else {
                logger.error("没有可用的数据库连接源");
            }
        } else {
            jdbc_switch = true;
        }
    }


    private static boolean checkJdbcDataSource(DataSource dataSource) {
        if (null == dataSource) {
            return false;
        }
        try {
            Connection connection = dataSource.getConnection();
            if (connection != null) {
                connection.close();
            }
            return true;
        } catch (SQLException e) {
            return false;
        }
    }

    // 获取数据库连接
    public static Connection getConnection() throws SQLException {
        Connection conn = tl.get();
        if (conn != null) {
            tl_connCount.set(tl_connCount.get() + 1);
            return conn;
        }
        Connection connection;
        if (jdbc_switch) {
            connection = dataSourceMaster.getConnection();
        } else {
            connection = dataSourceSlave.getConnection();
        }
        tl.set(connection);
        tl_connCount.set(1);
        return connection;
    }

    public static Properties getDBProps() {
        return dbProps;
    }

    // 设置事务手动提交
    public static void beginTransaction(Connection conn) throws SQLException {
        if (conn != null) {
            if (conn.getAutoCommit()) {
                conn.setAutoCommit(false);
            }
        }
    }

    // 提交事务
    public static void commit(Connection conn) throws SQLException {
        if (conn != null) {
            if (!conn.getAutoCommit()) {
                conn.commit();
                conn.setAutoCommit(true);
            }
        }
    }

    // 发生异常回滚事务
    public static void rollback(Connection conn) throws SQLException {
        if (conn != null) {
            if (!conn.getAutoCommit()) {
                conn.rollback();
                conn.setAutoCommit(true);
            }
        }
    }

    // 关闭连接,并将其从当前线程删除
    public static void close(Connection conn) throws SQLException {
        if (conn != null) {
            if (tl_connCount.get() == 1) {
                conn.close();
                tl.remove();
                tl_connCount.remove();
            } else {
                tl_connCount.set(tl_connCount.get() - 1);
            }
        }
    }

}
